#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Redis缓存管理类
用于缓存股票历史数据，避免重复API调用
"""

import redis
import json
import logging
from datetime import date, timedelta
from typing import Dict, Optional, List, Any
from config.config import REDIS_CONFIG


class RedisManager:
    """Redis缓存管理类"""
    
    def __init__(self):
        self.logger = logging.getLogger(__name__)
        self.redis_client = None
        self._connect()
    
    def _connect(self):
        """连接Redis服务器"""
        try:
            self.redis_client = redis.Redis(
                host=REDIS_CONFIG.get('host', 'localhost'),
                port=REDIS_CONFIG.get('port', 6379),
                db=REDIS_CONFIG.get('db', 0),
                password=REDIS_CONFIG.get('password', None),
                decode_responses=True,  # 自动解码响应为字符串
                socket_timeout=REDIS_CONFIG.get('timeout', 5),
                socket_connect_timeout=REDIS_CONFIG.get('connect_timeout', 5)
            )
            
            # 测试连接
            self.redis_client.ping()
            self.logger.info("Redis连接成功")
            
        except redis.ConnectionError as e:
            self.logger.error(f"Redis连接失败: {e}")
            self.redis_client = None
        except Exception as e:
            self.logger.error(f"Redis初始化异常: {e}")
            self.redis_client = None
    
    def is_available(self) -> bool:
        """检查Redis是否可用"""
        if not self.redis_client:
            return False
        
        try:
            self.redis_client.ping()
            return True
        except:
            return False
    
    # ==================== 股票历史成交量缓存 ====================
    
    def get_stock_volume(self, stock_code: str, target_date: date) -> Optional[int]:
        """
        从缓存获取股票指定日期的成交量
        
        Args:
            stock_code: 股票代码
            target_date: 目标日期
            
        Returns:
            成交量，如果不存在返回None
        """
        if not self.is_available():
            return None
        
        try:
            key = self._get_volume_cache_key(stock_code, target_date)
            value = self.redis_client.get(key)
            
            if value is not None:
                volume = int(value)
                self.logger.debug(f"从缓存获取 {stock_code} 在 {target_date} 的成交量: {volume}")
                return volume
            
            return None
            
        except Exception as e:
            self.logger.error(f"从缓存获取股票成交量失败: {e}")
            return None
    
    def set_stock_volume(self, stock_code: str, target_date: date, volume: int, expire_days: int = 30) -> bool:
        """
        缓存股票指定日期的成交量
        
        Args:
            stock_code: 股票代码
            target_date: 目标日期
            volume: 成交量
            expire_days: 过期天数，默认30天
            
        Returns:
            是否成功
        """
        if not self.is_available() or volume is None:
            return False
        
        try:
            key = self._get_volume_cache_key(stock_code, target_date)
            expire_seconds = expire_days * 24 * 60 * 60
            
            result = self.redis_client.setex(key, expire_seconds, str(volume))
            
            if result:
                self.logger.debug(f"缓存股票成交量: {stock_code} {target_date} = {volume}")
                return True
            
            return False
            
        except Exception as e:
            self.logger.error(f"缓存股票成交量失败: {e}")
            return False
    
    def get_batch_stock_volumes(self, stock_codes: List[str], target_date: date) -> Dict[str, Optional[int]]:
        """
        批量从缓存获取股票成交量
        
        Args:
            stock_codes: 股票代码列表
            target_date: 目标日期
            
        Returns:
            股票代码到成交量的映射
        """
        result = {}
        
        if not self.is_available() or not stock_codes:
            return result
        
        try:
            # 构建批量查询的keys
            keys = [self._get_volume_cache_key(code, target_date) for code in stock_codes]
            
            # 批量获取
            values = self.redis_client.mget(keys)
            
            # 构建结果映射
            for i, (code, value) in enumerate(zip(stock_codes, values)):
                if value is not None:
                    try:
                        result[code] = int(value)
                        self.logger.debug(f"从缓存获取 {code}: {result[code]}")
                    except ValueError:
                        result[code] = None
                else:
                    result[code] = None
            
            hit_count = sum(1 for v in result.values() if v is not None)
            self.logger.info(f"缓存命中: {hit_count}/{len(stock_codes)} 只股票")
            
            return result
            
        except Exception as e:
            self.logger.error(f"批量获取缓存失败: {e}")
            return {}
    
    def set_batch_stock_volumes(self, volumes_data: Dict[str, int], target_date: date, expire_days: int = 30) -> int:
        """
        批量缓存股票成交量
        
        Args:
            volumes_data: 股票代码到成交量的映射
            target_date: 目标日期
            expire_days: 过期天数
            
        Returns:
            成功缓存的数量
        """
        if not self.is_available() or not volumes_data:
            return 0
        
        try:
            pipe = self.redis_client.pipeline()
            expire_seconds = expire_days * 24 * 60 * 60
            success_count = 0
            
            for stock_code, volume in volumes_data.items():
                if volume is not None and volume >= 0:
                    key = self._get_volume_cache_key(stock_code, target_date)
                    pipe.setex(key, expire_seconds, str(volume))
                    success_count += 1
            
            # 执行批量操作
            pipe.execute()
            
            self.logger.info(f"批量缓存股票成交量: {success_count} 条记录")
            return success_count
            
        except Exception as e:
            self.logger.error(f"批量缓存失败: {e}")
            return 0
    
    def _get_volume_cache_key(self, stock_code: str, target_date: date) -> str:
        """生成成交量缓存key"""
        return f"stock_volume:{stock_code}:{target_date.strftime('%Y%m%d')}"

    # ==================== 股票名称缓存 (使用Hash) ====================

    def get_stock_names(self, stock_codes: List[str]) -> Dict[str, Optional[str]]:
        """
        从Redis Hash中批量获取股票名称
        
        Args:
            stock_codes: 股票代码列表
            
        Returns:
            股票代码到名称的映射，未找到的股票值为None
        """
        result: Dict[str, Optional[str]] = {code: None for code in stock_codes}
        if not self.is_available() or not stock_codes:
            return result
        
        try:
            key = self._get_stock_name_cache_key()
            
            # 使用hmget批量获取
            names = self.redis_client.hmget(key, stock_codes)
            
            for code, name in zip(stock_codes, names):
                if name:
                    result[code] = name
            
            hit_count = sum(1 for name in result.values() if name is not None)
            self.logger.info(f"股票名称缓存命中: {hit_count}/{len(stock_codes)}")
            return result

        except Exception as e:
            self.logger.error(f"批量获取股票名称缓存失败: {e}")
            return result

    def set_stock_names(self, name_data: Dict[str, str]) -> int:
        """
        将股票代码和名称的映射批量存入Redis Hash
        
        Args:
            name_data: 股票代码到名称的映射
            
        Returns:
            成功缓存的数量
        """
        if not self.is_available() or not name_data:
            return 0
        
        try:
            key = self._get_stock_name_cache_key()
            
            # 使用hmset批量设置
            self.redis_client.hmset(key, name_data)
            
            self.logger.info(f"批量缓存股票名称: {len(name_data)} 条记录")
            return len(name_data)
            
        except Exception as e:
            self.logger.error(f"批量缓存股票名称失败: {e}")
            return 0

    def _get_stock_name_cache_key(self) -> str:
        """生成股票名称缓存的Hash Key"""
        return "stock_names"

    # ==================== 通用缓存方法 ====================
    
    def get_cache(self, key: str) -> Optional[str]:
        """获取通用缓存"""
        if not self.is_available():
            return None
        
        try:
            return self.redis_client.get(key)
        except Exception as e:
            self.logger.error(f"获取缓存失败 {key}: {e}")
            return None
    
    def set_cache(self, key: str, value: str, expire_seconds: int = 3600) -> bool:
        """设置通用缓存"""
        if not self.is_available():
            return False
        
        try:
            return self.redis_client.setex(key, expire_seconds, value)
        except Exception as e:
            self.logger.error(f"设置缓存失败 {key}: {e}")
            return False
    
    def delete_cache(self, key: str) -> bool:
        """删除缓存"""
        if not self.is_available():
            return False
        
        try:
            return self.redis_client.delete(key) > 0
        except Exception as e:
            self.logger.error(f"删除缓存失败 {key}: {e}")
            return False
    
    def clear_expired_volumes(self, days_ago: int = 30):
        """清理过期的成交量缓存"""
        if not self.is_available():
            return
        
        try:
            cutoff_date = date.today() - timedelta(days=days_ago)
            pattern = f"stock_volume:*:{cutoff_date.strftime('%Y%m%d')}"
            
            keys = self.redis_client.keys(pattern)
            if keys:
                deleted = self.redis_client.delete(*keys)
                self.logger.info(f"清理过期缓存: {deleted} 条记录")
        except Exception as e:
            self.logger.error(f"清理过期缓存失败: {e}")
    
    def get_cache_stats(self) -> Dict[str, Any]:
        """获取缓存统计信息"""
        if not self.is_available():
            return {"status": "unavailable"}
        
        try:
            info = self.redis_client.info()
            return {
                "status": "connected",
                "used_memory": info.get("used_memory_human", "Unknown"),
                "connected_clients": info.get("connected_clients", 0),
                "total_commands_processed": info.get("total_commands_processed", 0),
                "keyspace_hits": info.get("keyspace_hits", 0),
                "keyspace_misses": info.get("keyspace_misses", 0)
            }
        except Exception as e:
            self.logger.error(f"获取缓存统计失败: {e}")
            return {"status": "error", "error": str(e)}


# 全局单例实例
redis_manager = RedisManager()